﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Xml;
using Castle.Core.Configuration;
using Castle.Core.Resource;
using Mbs.Reflection;

namespace Mbs.CastleContainer
{
    [Serializable]
    public class CastleContainer : MarshalByRefObject, IServiceLocator, IServiceRegistry
    {
        //[NonSerialized]
        private ComponentInstaller installer;
        private MbsKernel Kernel;
        //[NonSerialized]
        private XmlInterpreter interpreter = null;
        private List<CastleContainer> childs = new List<CastleContainer>();
        public IClassLoader ClassLoader { get; private set; }

        public CastleContainer()
            : this(null,new ClassLoader())
        {
        }

        public CastleContainer(IServiceLocator parent,IClassLoader classLoader)
        {

            Kernel = new MbsKernel();
            ClassLoader = classLoader;

            var castlePerent = parent as CastleContainer;
            if (parent != null)
            {
                Parent = parent;
                castlePerent.Kernel.AddChildKernel(Kernel);
                castlePerent.childs.Add(this);
                Root = castlePerent.Root;

                Kernel.AddComponentInstance(Guid.NewGuid().ToString(), typeof(IClassLoader), classLoader);
                Kernel.AddComponentInstance(Guid.NewGuid().ToString(), typeof(IServiceLocator), this);
                Kernel.AddComponentInstance(Guid.NewGuid().ToString(), typeof(IServiceRegistry), this);
            }
            else
            {
                Kernel.AddComponentInstance("root.classLoader", typeof(IClassLoader), classLoader);
                Kernel.AddComponentInstance("root.locator", typeof(IServiceLocator), this);
                Kernel.AddComponentInstance("root.service.registry", typeof(IServiceRegistry), this);
                Root = this;
            }

            installer = new ComponentInstaller();
            Load(string.Empty);
        }

        /// <summary>
        /// Run Installer
        /// </summary>
        private void RunInstaller()
        {
            if (installer != null)
            {
                installer.SetUp(Kernel, Kernel.ConfigurationStore);
            }
        }

        public void Load(string xmlFile)
        {
            if (File.Exists(xmlFile))
                interpreter = new XmlInterpreter(xmlFile);
            else if (Parent == null && ConfigurationManager.GetSection("castle") != null)
                interpreter = new XmlInterpreter(new ConfigResource());
            else
                interpreter = new XmlInterpreter();

            if (interpreter != null && interpreter.Source != null)
                interpreter.ProcessResource(interpreter.Source, Kernel.ConfigurationStore);

            RunInstaller();
        }

        public void Load(XmlNode node, IClassLoader classLoader)
        {
            if (node != null)
            {
                if (interpreter != null)
                {
                    interpreter.Process(node, Kernel.ConfigurationStore);
                    RunInstaller(node, classLoader);
                }
            }
            else
                Load(string.Empty);
        }

        public void UnLoad(XmlNode node)
        {
            XmlNode componentsNode = null;
            XmlNode facilitiesNode = null;
            if (node.FirstChild.Name == "components")
                componentsNode = node.ChildNodes[0];
            else if (node.FirstChild.Name == "facilities")
                facilitiesNode = node.ChildNodes[0];

            if (node.ChildNodes.Count == 2)
            {
                if (node.ChildNodes[1].Name == "components")
                    componentsNode = node.ChildNodes[1];
                else if (node.ChildNodes[1].Name == "facilities")
                    facilitiesNode = node.ChildNodes[1];
            }

            if (facilitiesNode != null)
            {
                foreach (XmlNode item in facilitiesNode.ChildNodes)
                    if (item.NodeType == XmlNodeType.Element)
                        Kernel.RemoveComponent(item.Attributes["id"].Value);
            }

            if (componentsNode != null)
            {
                foreach (XmlNode item in componentsNode.ChildNodes)
                    if (item.NodeType == XmlNodeType.Element)
                        UnRegister(item.Attributes["id"].Value);
            }
        }

        private void RunInstaller(XmlNode node, IClassLoader classLoader)
        {
            List<IConfiguration> componentsConfig = new List<IConfiguration>();
            List<IConfiguration> facilitiesConfig = new List<IConfiguration>();

            XmlNode componentsNode = null;
            XmlNode facilitiesNode = null;
            if (node.FirstChild.Name == "components")
                componentsNode = node.ChildNodes[0];
            else if (node.FirstChild.Name == "facilities")
                facilitiesNode = node.ChildNodes[0];

            if (node.ChildNodes.Count == 2)
            {
                if (node.ChildNodes[1].Name == "components")
                    componentsNode = node.ChildNodes[1];
                else if (node.ChildNodes[1].Name == "facilities")
                    facilitiesNode = node.ChildNodes[1];
            }

            if (facilitiesNode != null)
            {
                foreach (XmlNode item in facilitiesNode.ChildNodes)
                    if (item.NodeType == XmlNodeType.Element)
                        facilitiesConfig.Add(Kernel.ConfigurationStore.GetFacilityConfiguration(item.Attributes["id"].Value));
                installer.SetUpFacilities(facilitiesConfig.ToArray(), Kernel, classLoader);
            }
           
            if (componentsNode != null)
            {
                foreach (XmlNode item in componentsNode.ChildNodes)
                    if (item.NodeType == XmlNodeType.Element)
                        componentsConfig.Add(Kernel.ConfigurationStore.GetComponentConfiguration(item.Attributes["id"].Value));
                installer.SetUpComponents(componentsConfig.ToArray(), Kernel, classLoader);
            }
        }

        #region IServiceLocator Members

        public IServiceRegistry RegisterInstance(string id, object instance)
        {
            
            Kernel.AddComponentInstance(id, instance);
            return this;
        }

        public IServiceRegistry RegisterInstance(string id, Type service, object instance)
        {
            Kernel.AddComponentInstance(id, service, instance);
            return this;
        }

        public IServiceRegistry Register(Mbs.ComponentModel.Container.Metadata.IComponentInfo info)
        {
            throw new NotImplementedException();
        }

        public void UnRegister(object instance)
        {
            Kernel.ReleaseComponent(instance);
        }

      
        public void UnRegister(string id)
        {
            //var instance = kernel[id];
            Kernel.RemoveComponent(id);
            //if (instance != null)
            //{
            //    var dis = instance as IDisposable;
            //    if (dis != null)
            //        dis.Dispose();
            //    instance = null;
            //}
            //var configStore = kernel.GetSubSystem(SubSystemConstants.ConfigurationStoreKey) as IConfigurationStore;
            //var cfg = configStore.GetComponentConfiguration(id);
            //if (cfg != null)
            //{
            //    cfg.Attributes.Clear();
            //    cfg.Attributes.Add("id", Guid.NewGuid().ToString());
            //}
        }

        public void RemoveChildLocator(IServiceLocator locator)
        {
            var child = locator as CastleContainer;
            
            if (child != null)
            {
                if (childs.Contains(child))
                    childs.Remove(child);
            
                Kernel.RemoveChildKernel(child.Kernel);
                child.Parent = null;
                child.Root = null;
            }
        }

        public bool IsRoot
        {
            get { return this == root; }
        }

        private CastleContainer root;
        public IServiceLocator Root
        {
            get { return root; }
            private set { root = value as CastleContainer; }
        }

        public IServiceLocator Parent
        {
            get;
            private set;
        }

     
        public IEnumerable<IServiceLocator> Childs
        {
            get { return childs.ToArray(); }
        }

        public object Get(string id)
        {
            if (Kernel.HasComponent(id))
                return Kernel[id];

            if (root.Kernel.HasComponent(id))
                return root.Kernel[id];

            foreach(var child in root.childs)
                if (child.Kernel.HasComponent(id))
                    return child.Kernel[id];
            
            return null;
        }

        public object Get(Type serviceType)
        {
            if (Kernel.HasComponent(serviceType))
                return Kernel.Resolve(serviceType);

            if (root.Kernel.HasComponent(serviceType))
                return root.Kernel.Resolve(serviceType);

            foreach (var child in root.childs)
                if (child.Kernel.HasComponent(serviceType))
                    return child.Kernel.Resolve(serviceType);

            return null;
        }

        public object Get(Type serviceType, string id)
        {
            var o = Get(id);
            if (o == null)
                o = Get(serviceType);
            return o;
        }

        //public TService Get<TService>()
        //{
        //    return (TService)Get(typeof(TService));
        //}

        //public TService Get<TService>(string id)
        //{
        //    return (TService)Get(typeof(TService), id);
        //}

        #endregion

        #region IServiceProvider Members

        public object GetService(Type serviceType)
        {
            return Get(serviceType);
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (Parent != null)
            {
                var castleParent = Parent as CastleContainer;
                if (castleParent != null)
                    castleParent.RemoveChildLocator(this);
            }
            Kernel.Dispose();
        }

        #endregion

        #region IServiceRegistry Members

       

        #endregion
    }
}
